package com.jxtc.enterprise.admin.controller;

import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jxtc.enterprise.admin.dto.*;
import com.jxtc.enterprise.admin.vo.AdminBillLogVO;
import com.jxtc.enterprise.admin.vo.AdminCompanySettlementDetailVO;
import com.jxtc.enterprise.admin.vo.AdminCompanySettlementPageVO;
import com.jxtc.enterprise.admin.vo.BasicCompanySettlementInfoVO;
import com.jxtc.enterprise.common.constants.CompanySettlementStatusEnum;
import com.jxtc.enterprise.common.constants.OrderPaymentStatus;
import com.jxtc.enterprise.common.entity.Company;
import com.jxtc.enterprise.common.entity.CompanySettlement;
import com.jxtc.enterprise.common.entity.OrderDetail;
import com.jxtc.enterprise.common.entity.OrderPayment;
import com.jxtc.enterprise.common.exception.ModifyFailureException;
import com.jxtc.enterprise.common.mapper.CompanyMapper;
import com.jxtc.enterprise.common.mapper.CompanySettlementMapper;
import com.jxtc.enterprise.common.mapper.OrderDetailMapper;
import com.jxtc.enterprise.common.mapper.OrderMapper;
import com.jxtc.enterprise.common.utils.CurrencyUtil;
import com.jxtc.enterprise.common.utils.Result;
import com.jxtc.enterprise.common.utils.ResultGenerator;
import com.jxtc.enterprise.common.vo.PaginationResponseVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/jxtc/enterprise/admin/companySettle")
@RequiredArgsConstructor
@Slf4j
public class AdminCompanySettlementController {

    private final CompanySettlementMapper companySettlementMapper;
    private final CompanyMapper companyMapper;
    private final OrderMapper orderMapper;
    private final OrderDetailMapper orderDetailMapper;


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为结算状态字典列表")
    @Operation(summary = "22.1 获取结算状态下拉列表", tags = "22. 管理平台公司结算", operationId = "22.1")
    @GetMapping("/queryStatusList")
    public Result<?> queryStatusList() {
        // 将结算状态枚举类中的 name - value 转换成列表的形式
        List<Map<String, Object>> settlementStatusList = CompanySettlementStatusEnum.toList();
        return ResultGenerator.genSuccessResult(settlementStatusList);
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为包含财务结算列表的分页信息")
    @Operation(summary = "22.2 分页查询财务结算列表", tags = "22. 管理平台公司结算", operationId = "22.2")
    @PostMapping("/queryCompanySettlementList")
    public Result<PaginationResponseVO<AdminCompanySettlementPageVO>> queryCompanySettlementList(@Valid @RequestBody AdminCompanySettlementPageDTO dto) {
        log.info("/admin/queryCompanySettlementList, companyName: {}, startDate: {}, endDate: {}, status: {}, pageNo: {}, pageSize: {}",
                dto.getCompanyName(), dto.getStartDate(), dto.getEndDate(), dto.getStatus(), dto.getPageNo(), dto.getPageSize());

        Page<CompanySettlement> page = new Page<>(dto.getPageNo(), dto.getPageSize());

        LambdaQueryWrapper<CompanySettlement> queryWrapper = new LambdaQueryWrapper<CompanySettlement>()
                // 查询未删除的记录
                .eq(CompanySettlement::getDelFlag, false)
                // 根据 月份、结束时间、开始时间 降序排列，都相同的情况下按照记录创建时间降序排列
                .orderByDesc(CompanySettlement::getMonth, CompanySettlement::getEndDate, CompanySettlement::getStartDate, CompanySettlement::getCreateTime);

        // 如果公司名称不空，则拼接 %#{=companyName}%
        if (dto.getCompanyName() != null && !dto.getCompanyName().isEmpty()) {
            queryWrapper.like(CompanySettlement::getCompanyName, dto.getCompanyName());
        }

        // 如果开始时间不空，筛选结算开始时间 >= startDate 的记录
        if (dto.getStartDate() != null && !dto.getStartDate().isEmpty()) {
            queryWrapper.ge(CompanySettlement::getStartDate, dto.getStartDate());
        }

        // 如果结束时间不空，筛选结算结束时间 <= endDate 的记录
        if (dto.getEndDate() != null && !dto.getEndDate().isEmpty()) {
            queryWrapper.le(CompanySettlement::getEndDate, dto.getEndDate());
        }

        // 如果结算状态不空且不为 0，筛选结算状态 = status 的记录
        if (dto.getStatus() != null && dto.getStatus() != 0) {
            queryWrapper.eq(CompanySettlement::getStatus, dto.getStatus());
        }

        Page<CompanySettlement> result = companySettlementMapper.selectPage(page, queryWrapper);
        List<AdminCompanySettlementPageVO> records = result.getRecords().stream()
                .map(this::buildCompanySettlementPageVO)
                .collect(Collectors.toList());

        PaginationResponseVO<AdminCompanySettlementPageVO> vo = new PaginationResponseVO<>(result.getTotal(), result.getCurrent(), records);
        log.info("/admin/queryCompanySettlementList, {}", vo);

        return ResultGenerator.genSuccessResult(vo);
    }

    private AdminCompanySettlementPageVO buildCompanySettlementPageVO(CompanySettlement companySettlement) {
        AdminCompanySettlementPageVO vo = new AdminCompanySettlementPageVO();
        vo.setId(companySettlement.getId());
        vo.setCompanyName(companySettlement.getCompanyName());
        vo.setMonth(companySettlement.getMonth());
        vo.setStartDate(companySettlement.getStartDate());
        vo.setEndDate(companySettlement.getEndDate());

        // 获取提示日期，如果表中存放的提示提前天数为null，则默认提前1天提示
        Integer noteDay = companySettlement.getNoteDay() == null ? 1 : companySettlement.getNoteDay();
        vo.setNoteDay(noteDay);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate targetDate = LocalDate.parse(companySettlement.getEndDate(), formatter);
        LocalDate currentDate = LocalDate.now();
        // 计算当前日期到目标日期还剩多少天
        long remainingDays = ChronoUnit.DAYS.between(currentDate, targetDate);
        vo.setRemainingDays(remainingDays);

        Integer statusValue = companySettlement.getStatus();
        vo.setStatusValue(statusValue);
        vo.setStatusName(CompanySettlementStatusEnum.getNameByValue(statusValue));
        vo.setBillUrl(companySettlement.getBillUrl());
        return vo;
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为公司及其对应的 ID 的字典列表")
    @Operation(summary = "22.3 查询公司字典列表", tags = "22. 管理平台公司结算", operationId = "22.3")
    @GetMapping("/queryCompanyList")
    public Result<List<?>> queryCompanyList() {
        LambdaQueryWrapper<Company> queryWrapper = new LambdaQueryWrapper<Company>()
                .eq(Company::getDelFlag, false)
                .orderByDesc(Company::getCreateTime);

        List<Company> companyList = companyMapper.selectList(queryWrapper);
        // 将查询到的公司列表转换成公司名称及公司 ID 的字典列表
        List<Map<String, String>> voList = companyList.stream()
                .map(this::buildCompanyNameWithId)
                .collect(Collectors.toList());

        return ResultGenerator.genSuccessResult(voList);
    }

    private Map<String, String> buildCompanyNameWithId(Company company) {
        Map<String, String> companyNameWithIdMap = new HashMap<>(1);
        companyNameWithIdMap.put("companyId", company.getId());
        companyNameWithIdMap.put("companyName", company.getName());
        return companyNameWithIdMap;
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Operation(summary = "22.4 新增结算记录", tags = "22. 管理平台公司结算", operationId = "22.4")
    @PostMapping("/addCompanySettlementRecord")
    public Result<Void> addCompanySettlementRecord(@Valid @RequestBody AddCompanySettlementRecordDTO dto) {
        log.info("/admin/addCompanySettlementRecord, companyId: {}, companyName: {}, month: {}, startDate: {}, endDate: {}, noteDay: {}",
                dto.getCompanyId(), dto.getCompanyName(), dto.getMonth(), dto.getStartDate(), dto.getEndDate(), dto.getNoteDay());

        Company company = companyMapper.selectById(dto.getCompanyId());
        if (company == null) {
            throw new IllegalArgumentException("ID 为 " + dto.getCompanyId() + " 的公司不存在，请联系管理员排查");
        }

        // 构建公司结算记录，计算订单份数及金额，生成 excel 文件并上传到 oss 服务
        CompanySettlement companySettlement = buildCompanySettlement(dto.getCompanyId(), dto.getMonth(), dto.getStartDate(), dto.getEndDate(), dto.getNoteDay());
        companySettlement.setCompanyId(dto.getCompanyId());
        companySettlement.setCompanyName(company.getName());

        // 向数据库添加数据，添加失败时给前端提供提示信息
        int insertCount = companySettlementMapper.insert(companySettlement);
        log.info("/admin/addCompanySettlementRecord, insert count: {}", insertCount);

        if (insertCount <= 0) {
            throw new ModifyFailureException("新增结算记录失败，请联系管理员排查");
        }

        return ResultGenerator.genSuccessResult();
    }

    private CompanySettlement buildCompanySettlement(String companyId, String month, String startDate, String endDate, Integer noteDay) {
        CompanySettlement companySettlement = new CompanySettlement();
        companySettlement.setMonth(month);
        companySettlement.setStartDate(startDate);
        companySettlement.setEndDate(endDate);
        companySettlement.setNoteDay(noteDay);

        LambdaQueryWrapper<OrderPayment> orderQueryWrapper = new LambdaQueryWrapper<OrderPayment>()
                .eq(OrderPayment::getCompanyId, companyId)
                .eq(OrderPayment::getStatus, OrderPaymentStatus.COMPLETED.getValue())
                .between(OrderPayment::getDateFlag, startDate, endDate)
                .orderByDesc(OrderPayment::getCreateTime);

        List<OrderPayment> orderPayments = orderMapper.selectList(orderQueryWrapper);
        // 订单总份数，没一份订单中可能包含多项餐品（目前来看通常是 1 份）
        int totalOrderCount = orderPayments.size();
        int orderMoneyInFen = 0;

        List<String> orderIds = new ArrayList<>();
        for (OrderPayment orderPayment : orderPayments) {
            orderIds.add(orderPayment.getId());

            // 理论上 orderPayment.getTotalAmount() - orderPayment.getUserPaid() 应该等于 orderPayment.getDeductionPrice()
            // 但是实际上业务员在平台管理填写菜品 sale_price 时经常和 show_price 一起输入为 0
            // 此时 orderPayment.getDeductionPrice() 的值在数据库中也为 0
            // 所以使用 "订单金额 - 用户自费金额" 来代替 "用户使用余额支付" 的部分
            // "用户使用余额支付" 的部分才是需要向 HR 结算的金额
            orderMoneyInFen += orderPayment.getTotalAmount() - orderPayment.getUserPaid();
        }

        companySettlement.setTotalOrderCount(totalOrderCount);
        companySettlement.setOrderMoney(orderMoneyInFen);

        // 生成账单 excel 文件并上传到 oss 中，然后将路径回填到 bill_url 中
        String billUrl = buildExcelAndUploadFileToOSS(orderIds, orderPayments);
        companySettlement.setBillUrl(billUrl);

        return companySettlement;
    }

    private String buildExcelAndUploadFileToOSS(List<String> orderIds, List<OrderPayment> orderPayments) {
        // 如果订单列表为空，则返回一个空的 excel 文件（该文件已经上传到 oss 服务中了）
        final String emptyFileInOssUrl = "https://yong-shan-tuan.oss-cn-hangzhou.aliyuncs.com/ab92f5cb-32bc-49e2-98b5-b6a7272eef44.xls";
        if (orderIds == null || orderIds.isEmpty()) {
            log.info("订单 ID 列表为空，不需要构建 excel 文件");
            return emptyFileInOssUrl;
        }

        LambdaQueryWrapper<OrderDetail> orderDetailQueryWrapper = new LambdaQueryWrapper<OrderDetail>()
                .in(OrderDetail::getOrderId, orderIds);
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailQueryWrapper);
        // 根据 orderId 分组，方便后续根据 orderId 直接获取该订单下的订单详情信息列表
        Map<String, List<OrderDetail>> orderDetailMap = orderDetails.stream()
                .collect(Collectors.groupingBy(OrderDetail::getOrderId));

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try (ExcelWriter writer = ExcelUtil.getWriter(true)) {
            List<Map<String, Object>> data = new ArrayList<>();

            int idx = 1;
            for (OrderPayment orderPayment : orderPayments) {
                String orderId = orderPayment.getId();
                List<OrderDetail> orderDetailList = orderDetailMap.get(orderId);
                // 用餐时间
                String dateFlag = orderPayment.getDateFlag();
                // 用餐类型（午餐还是晚餐）
                String mealFlag = orderPayment.getMealFlag().split(" ")[0];
                // 同一笔订单中所有订单详情的 username 是一样的，取其一即可
                String username = orderDetailList.get(0).getUsername();
                // 使用 ", " 将每一份订单详情中的餐品名称拼接起来
                String dishName = orderDetailList.stream().map(OrderDetail::getFoodPrintName).collect(Collectors.joining(", "));

                int priceInFen = orderDetailList.stream().mapToInt(OrderDetail::getSalePrice).sum();
                String priceInYuan = CurrencyUtil.fenToYuan(priceInFen);
                // 记录订单创建时间
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String orderTime = orderPayment.getCreateTime().format(formatter);

                data.add(createRow(idx++, dateFlag, mealFlag, username, dishName, priceInYuan, orderTime));
            }

            writer.write(data, true);
            // 获取当前工作表
            Sheet sheet = writer.getSheet();
            // 设置“用餐时间”这一列的宽度，索引从 0 开始
            int dateFlagColumnIndex = 1;
            sheet.setColumnWidth(dateFlagColumnIndex, 12 * 256);
            // 设置“餐品”这一列的宽度，索引从 0 开始
            int dishNameColumnIndex = 4;
            sheet.setColumnWidth(dishNameColumnIndex, 60 * 256);
            // 设置“下单时间”这一列的宽度
            int orderTimeColumnIndex = 6;
            sheet.setColumnWidth(orderTimeColumnIndex, 20 * 256);

            // 将生成的 Excel 写入 ByteArrayOutputStream
            writer.flush(out);
        }

        // 将生成的 Excel 上传到 OSS
        byte[] excelBytes = out.toByteArray();
        InputStream inputStream = new ByteArrayInputStream(excelBytes);

        // 使用随机的 UUID 作为文件名
        String filename = UUID.randomUUID() + ".xls";

        final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        final String accessKeyId = "LTAI5t73gzGH975QGrhBpUqK";
        final String accessKeySecret = "Kyt6Pc6UN3IfBxkXgC6R2X0OsB1dLh";
        final String bucketName = "yong-shan-tuan";

        // 上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, filename, inputStream);
        // 文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + filename;
        // 关闭 ossClient
        ossClient.shutdown();

        log.info("文件上传完成，路径为：{}", url);

        return url;
    }

    private Map<String, Object> createRow(Integer index, String dateFlag, String mealFlag, String username, String dish, String price, String orderTime) {
        // 使用 LinkedHashMap 而不是 HashMap 防止标题栏乱序
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("序号", index);
        row.put("用餐时间", dateFlag);
        row.put("用餐类型", mealFlag);
        row.put("用户名", username);
        row.put("餐品", dish);
        row.put("价格", new BigDecimal(price));
        row.put("下单时间", orderTime);
        return row;
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为结算记录基本信息；code 为 400 时，message 字段给出错误提示信息")
    @Parameter(name = "settleId", description = "公司结算记录 ID", example = "26e7c8b17867fa08584f92e1638b8eba")
    @Operation(summary = "22.5 查询结算记录基本信息", tags = "22. 管理平台公司结算", operationId = "22.5")
    @GetMapping("/queryCompanySettlementBasicInfo")
    public Result<BasicCompanySettlementInfoVO> queryCompanySettlementBasicInfo(@RequestParam String settleId) {
        log.info("/admin/queryCompanySettlementBasicInfo, settleId: {}", settleId);
        CompanySettlement companySettlement = companySettlementMapper.selectById(settleId);
        if (companySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + settleId + " 的结算记录不存在，请联系管理员排查");
        }

        BasicCompanySettlementInfoVO vo = new BasicCompanySettlementInfoVO();
        vo.setSettleId(companySettlement.getId());
        vo.setCompanyId(companySettlement.getCompanyId());
        vo.setCompanyName(companySettlement.getCompanyName());
        vo.setMonth(companySettlement.getMonth());
        vo.setStartDate(companySettlement.getStartDate());
        vo.setEndDate(companySettlement.getEndDate());
        vo.setNoteDay(companySettlement.getNoteDay());

        return ResultGenerator.genSuccessResult(vo);
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，表示编辑成功，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Operation(summary = "22.6 编辑结算记录", tags = "22. 管理平台公司结算", operationId = "22.6")
    @PostMapping("/editCompanySettlementRecord")
    public Result<Void> editCompanySettlementRecord(@Valid @RequestBody EditCompanySettlementRecordDTO dto) {
        log.info("/admin/editCompanySettlementRecord, settleId: {}, month: {}, startDate: {}, endDate: {}, noteDay: {}",
                dto.getSettleId(), dto.getMonth(), dto.getStartDate(), dto.getEndDate(), dto.getNoteDay());

        CompanySettlement savedCompanySettlement = companySettlementMapper.selectById(dto.getSettleId());
        if (savedCompanySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + dto.getSettleId() + " 的结算记录不存在，请联系管理员排查");
        }

        // 如果当前结算记录的状态为 "已结算"，则不允许编辑
        if (CompanySettlementStatusEnum.SETTLED.getValue() == savedCompanySettlement.getStatus()) {
            throw new ModifyFailureException("ID 为 " + dto.getSettleId() + " 的结算记录不允许编辑");
        }

        CompanySettlement updateCompanySettlement = new CompanySettlement();
        updateCompanySettlement.setId(dto.getSettleId());

        String month = savedCompanySettlement.getMonth();
        if (dto.getMonth() != null && !dto.getMonth().isEmpty()) {
            month = dto.getMonth();
            updateCompanySettlement.setMonth(dto.getMonth());
        }

        String startDate = savedCompanySettlement.getStartDate();
        if (dto.getStartDate() != null && !dto.getStartDate().isEmpty()) {
            startDate = dto.getStartDate();
            updateCompanySettlement.setStartDate(dto.getStartDate());
        }

        String endDate = savedCompanySettlement.getEndDate();
        if (dto.getEndDate() != null && !dto.getEndDate().isEmpty()) {
            endDate = dto.getEndDate();
            updateCompanySettlement.setEndDate(dto.getEndDate());
        }

        Integer noteDay = savedCompanySettlement.getNoteDay();
        if (dto.getNoteDay() != null) {
            noteDay = dto.getNoteDay();
            updateCompanySettlement.setNoteDay(dto.getNoteDay());
        }

        // 判断是否修改了结算周期（即：是否修改了 "结算开始时间" 或 "结算结束时间"）
        // 如果修改了结算周期，那么订单的总份数、总金额、以及订单详情文件都需要重新计算和生成，然后上传到 OSS 服务
        // 否则，直接修改数据库中记录即可
        boolean hasEditedDate = (dto.getStartDate() != null && !dto.getStartDate().isEmpty())
                || (dto.getEndDate() != null && !dto.getEndDate().isEmpty());
        log.info("/admin/editCompanySettlementRecord, hasEditedDate：{}", hasEditedDate);

        if (hasEditedDate) {
            log.info("/admin/editCompanySettlementRecord, 更新了结束时间范围，重新计算订单相关数据");
            updateCompanySettlement = buildCompanySettlement(savedCompanySettlement.getCompanyId(), month, startDate, endDate, noteDay);
            updateCompanySettlement.setId(dto.getSettleId());
            log.info("/admin/editCompanySettlementRecord, 重新计算后的结算记录: {}", updateCompanySettlement);
        }

        // 向数据库更新数据，更新失败时给前端提供提示信息
        int updateCount = companySettlementMapper.updateById(updateCompanySettlement);
        log.info("/admin/editCompanySettlementRecord, update count: {}", updateCount);

        if (updateCount <= 0) {
            throw new ModifyFailureException("更新失败，请稍后重试");
        }

        return ResultGenerator.genSuccessResult();
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，表示删除结算记录成功，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Parameter(name = "settleId", description = "公司结算记录 ID", example = "26e7c8b17867fa08584f92e1638b8eba")
    @Operation(summary = "22.7 删除结算记录", tags = "22. 管理平台公司结算", operationId = "22.7")
    @GetMapping("/deleteCompanySettlement")
    public Result<Void> deleteCompanySettlement(@RequestParam String settleId) {
        log.info("/admin/deleteCompanySettlement, settleId: {}", settleId);

        CompanySettlement companySettlement = companySettlementMapper.selectById(settleId);
        if (companySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + settleId + " 的结算记录不存在，请联系管理员排查");
        }

        // 如果当前结算记录的状态为 "已结算"，则不允许删除
        if (CompanySettlementStatusEnum.SETTLED.getValue() == companySettlement.getStatus()) {
            throw new ModifyFailureException("ID 为 " + settleId + " 的结算记录不允许删除");
        }

        CompanySettlement updateCompanySettlement = new CompanySettlement();
        updateCompanySettlement.setId(settleId);
        updateCompanySettlement.setDelFlag(true);

        int updateCount = companySettlementMapper.updateById(updateCompanySettlement);
        log.info("/admin/deleteCompanySettlement, update count: {}", updateCount);

        if (updateCount <= 0) {
            throw new ModifyFailureException("ID 为 " + settleId + " 的结算记录删除失败，请联系管理员排查");
        }

        return ResultGenerator.genSuccessResult();
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，data 字段为结算详情信息；code 为 400 时，message 字段给出错误提示信息")
    @Parameter(name = "settleId", description = "公司结算记录 ID", example = "26e7c8b17867fa08584f92e1638b8eba")
    @Operation(summary = "22.8 查询结算详情", tags = "22. 管理平台公司结算", operationId = "22.8")
    @GetMapping("/queryCompanySettlementDetails")
    public Result<AdminCompanySettlementDetailVO> queryCompanySettlementDetails(@RequestParam String settleId) {
        log.info("/admin/queryCompanySettlementDetails, settleId: {}", settleId);

        LambdaQueryWrapper<CompanySettlement> queryWrapper = new LambdaQueryWrapper<CompanySettlement>()
                .eq(CompanySettlement::getId, settleId)
                .eq(CompanySettlement::getDelFlag, false);

        CompanySettlement companySettlement = companySettlementMapper.selectOne(queryWrapper);

        if (companySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + settleId + " 的结算记录不存在，请联系管理员排查");
        }

        // 如果当前查询的结算记录处于 "未结算" 状态，提示前端 "未结算的记录无法查看详情"
        // if (companySettlement.getStatus() == CompanySettlementStatusEnum.NOT_SETTLED.getValue()) {
        //    throw new IllegalArgumentException("未结算的记录无法查看详情");
        // }

        AdminCompanySettlementDetailVO vo = new AdminCompanySettlementDetailVO();
        vo.setSettleId(settleId);

        String billPeriod = String.format("%s ~ %s", companySettlement.getStartDate(), companySettlement.getEndDate());
        vo.setBillPeriod(billPeriod);
        vo.setTotalOrderCount(companySettlement.getTotalOrderCount());

        String orderMoneyInYuan = CurrencyUtil.fenToYuan(companySettlement.getOrderMoney());
        vo.setOrderMoney(orderMoneyInYuan);

        String additionalMoneyInYuan = CurrencyUtil.fenToYuan(companySettlement.getAdditionalMoney());
        vo.setAdditionalMoney(additionalMoneyInYuan);

        String deductionMoneyInYuan = CurrencyUtil.fenToYuan(companySettlement.getDeductionMoney());
        vo.setDeductionMoney(deductionMoneyInYuan);
                
        // 扣款项取绝对值，避免页面输入的时候输入了负数
        int billTotalMoneyInFen = companySettlement.getOrderMoney() + companySettlement.getAdditionalMoney() - Math.abs(companySettlement.getDeductionMoney());
        String billTotalMoneyInYuan = CurrencyUtil.fenToYuan(billTotalMoneyInFen);
        vo.setBillTotalMoney(billTotalMoneyInYuan);

        List<String> invoiceImages = JSONUtil.toList(companySettlement.getInvoiceImages(), String.class);
        vo.setInvoiceImages(invoiceImages);

        List<String> transferImages = JSONUtil.toList(companySettlement.getTransferImages(), String.class);
        vo.setTransferImages(transferImages);

        List<AdminBillLogVO> billLogs = JSONUtil.toList(companySettlement.getBillLog(), AdminBillLogVO.class);
        vo.setBillLogs(billLogs);

        Integer statusValue = companySettlement.getStatus();
        vo.setStatusName(CompanySettlementStatusEnum.getNameByValue(statusValue));
        vo.setStatusValue(statusValue);

        log.info("/admin/queryCompanySettlementDetails, 响应结果: {}", vo);

        return ResultGenerator.genSuccessResult(vo);
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，表示提交结算订单成功，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Operation(summary = "22.9 提交结算订单", tags = "22. 管理平台公司结算", operationId = "22.9")
    @PostMapping("/submitCompanySettlement")
    public Result<Void> submitCompanySettlement(@Valid @RequestBody SubmitCompanySettlementDTO dto) {
        log.info("/admin/submitCompanySettlement, settleId: {}, additionalMoney: {}, deductionMoney: {}, comment: {}",
                dto.getSettleId(), dto.getAdditionalMoney(), dto.getDeductionMoney(), dto.getComment());

        CompanySettlement savedCompanySettlement = companySettlementMapper.selectById(dto.getSettleId());
        if (savedCompanySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + dto.getSettleId() + " 的结算记录不存在，请联系管理员排查");
        }

        CompanySettlement companySettlement = new CompanySettlement();
        companySettlement.setId(savedCompanySettlement.getId());

        if (dto.getAdditionalMoney() != null && !dto.getAdditionalMoney().isEmpty()) {
            BigDecimal additionalMoneyInYuan = new BigDecimal(dto.getAdditionalMoney());
            int additionalMoneyInFen = CurrencyUtil.yuanToFen(additionalMoneyInYuan);
            companySettlement.setAdditionalMoney(additionalMoneyInFen);
        }

        if (dto.getDeductionMoney() != null && !dto.getDeductionMoney().isEmpty()) {
            BigDecimal deductionMoneyInYuan = new BigDecimal(dto.getDeductionMoney());
            int deductionMoneyInFen = CurrencyUtil.yuanToFen(deductionMoneyInYuan);
            companySettlement.setDeductionMoney(deductionMoneyInFen);
        }

        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        String comment = "业务人员发起结算";
        if (dto.getComment() != null && !dto.getComment().isEmpty()) {
            comment += "，备注：" + dto.getComment();
        }

        List<AdminBillLogVO> billLogs = JSONUtil.toList(savedCompanySettlement.getBillLog(), AdminBillLogVO.class);
        AdminBillLogVO adminBillLogVO = new AdminBillLogVO(now.format(formatter), comment);
        billLogs.add(adminBillLogVO);

        String billLogStr = JSONUtil.toJsonStr(billLogs);
        companySettlement.setBillLog(billLogStr);

        // 将结算状态设置为 "待 HR 确认"
        companySettlement.setStatus(CompanySettlementStatusEnum.PENDING_HR_CONFIRMATION.getValue());

        int updateCount = companySettlementMapper.updateById(companySettlement);
        log.info("/admin/submitCompanySettlement, update count: {}", updateCount);

        if (updateCount <= 0) {
            throw new ModifyFailureException("提交结算订单失败，请联系管理员排查");
        }

        return ResultGenerator.genSuccessResult();
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，表示补全结算附件成功，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Operation(summary = "22.10 补全结算附件", tags = "22. 管理平台公司结算", operationId = "22.10")
    @PostMapping("/complementCompanySettlement")
    public Result<Void> complementCompanySettlement(@Valid @RequestBody ComplementCompanySettlementDTO dto) {
        log.info("/admin/complementCompanySettlement, settleId: {}, invoiceImages: {}, transferImages: {}, comment: {}",
                dto.getSettleId(), dto.getInvoiceImages(), dto.getTransferImages(), dto.getComment());

        if (dto.getInvoiceImages() != null && dto.getInvoiceImages().size() > 5) {
            throw new IllegalArgumentException("发票凭证图片最多 5 张");
        }

        if (dto.getTransferImages() != null && dto.getTransferImages().size() > 5) {
            throw new IllegalArgumentException("转账凭证图片最多 5 张");
        }

        CompanySettlement savedCompanySettlement = companySettlementMapper.selectById(dto.getSettleId());
        if (savedCompanySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + dto.getSettleId() + " 的结算记录不存在，请联系管理员排查");
        }

        CompanySettlement companySettlement = new CompanySettlement();
        companySettlement.setId(savedCompanySettlement.getId());

        if (dto.getInvoiceImages() != null) {
            String invoiceImageStr = JSONUtil.toJsonStr(dto.getInvoiceImages());
            companySettlement.setInvoiceImages(invoiceImageStr);
        }

        if (dto.getTransferImages() != null) {
            String transferImageStr = JSONUtil.toJsonStr(dto.getTransferImages());
            companySettlement.setTransferImages(transferImageStr);
        }

        // 将备注和当前时间追加到结算流程日志中
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String comment = "业务人员上传凭证";
        if (dto.getComment() != null && !dto.getComment().isEmpty()) {
            comment += "，备注：" + dto.getComment();
        }
        AdminBillLogVO adminBillLogVO = new AdminBillLogVO(now.format(formatter), comment);
        List<AdminBillLogVO> billLogs = JSONUtil.toList(savedCompanySettlement.getBillLog(), AdminBillLogVO.class);
        billLogs.add(adminBillLogVO);
        String billLogStr = JSONUtil.toJsonStr(billLogs);
        companySettlement.setBillLog(billLogStr);

        // 将结算状态设置为 "已结算"
        companySettlement.setStatus(CompanySettlementStatusEnum.SETTLED.getValue());

        int updateCount = companySettlementMapper.updateById(companySettlement);
        log.info("/admin/complementCompanySettlement, update count: {}", updateCount);

        if (updateCount <= 0) {
            throw new ModifyFailureException("补全结算附件失败，请联系管理员排查");
        }

        return ResultGenerator.genSuccessResult();
    }


    @ApiResponse(responseCode = "200", description = "code 为 200 时，表示提交业务人员复核订单成功，data 字段为空；code 为 400 时，message 字段给出错误提示信息")
    @Operation(summary = "22.11 业务人员复核订单，同意或驳回，补充描述", tags = "22. 管理平台公司结算", operationId = "22.11")
    @PostMapping("/auditCompanySettlement")
    public Result<Void> auditCompanySettlement(@Valid @RequestBody AuditCompanySettlementDTO dto) {
        log.info("/admin/auditCompanySettlement, settleId: {}, additionalMoney: {}, deductionMoney: {}, comment: {}, auditResult: {}",
                dto.getSettleId(), dto.getAdditionalMoney(), dto.getDeductionMoney(), dto.getComment(), dto.getAuditResult());


        CompanySettlement savedCompanySettlement = companySettlementMapper.selectById(dto.getSettleId());
        if (savedCompanySettlement == null) {
            throw new IllegalArgumentException("ID 为 " + dto.getSettleId() + " 的结算记录不存在，请联系管理员排查");
        }

        CompanySettlement companySettlement = new CompanySettlement();
        companySettlement.setId(savedCompanySettlement.getId());

        if (dto.getAdditionalMoney() != null && !dto.getAdditionalMoney().isEmpty()) {
            BigDecimal additionalMoneyInYuan = new BigDecimal(dto.getAdditionalMoney());
            int additionalMoneyInFen = CurrencyUtil.yuanToFen(additionalMoneyInYuan);
            companySettlement.setAdditionalMoney(additionalMoneyInFen);
        }

        if (dto.getDeductionMoney() != null && !dto.getDeductionMoney().isEmpty()) {
            BigDecimal deductionMoneyInYuan = new BigDecimal(dto.getDeductionMoney());
            int deductionMoneyInFen = CurrencyUtil.yuanToFen(deductionMoneyInYuan);
            companySettlement.setDeductionMoney(deductionMoneyInFen);
        }

        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        String comment = "业务人员不同意驳回";
        if ("agree".equals(dto.getAuditResult())) {
            comment = "业务人员同意驳回";
        }

        if (dto.getComment() != null && !dto.getComment().isEmpty()) {
            comment += "，备注：" + dto.getComment();
        }

        List<AdminBillLogVO> billLogs = JSONUtil.toList(savedCompanySettlement.getBillLog(), AdminBillLogVO.class);
        AdminBillLogVO adminBillLogVO = new AdminBillLogVO(now.format(formatter), comment);
        billLogs.add(adminBillLogVO);

        String billLogStr = JSONUtil.toJsonStr(billLogs);
        companySettlement.setBillLog(billLogStr);

        // 将结算状态设置为 "待 HR 确认"
        companySettlement.setStatus(CompanySettlementStatusEnum.PENDING_HR_CONFIRMATION.getValue());

        int updateCount = companySettlementMapper.updateById(companySettlement);
        log.info("/admin/auditCompanySettlement, update count: {}", updateCount);

        if (updateCount <= 0) {
            throw new ModifyFailureException("提交结算订单失败，请联系管理员排查");
        }

        return ResultGenerator.genSuccessResult();
    }
}
